package automation {

import flash.display.DisplayObject;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.system.ApplicationDomain;
import flash.utils.describeType;
import flash.utils.getQualifiedClassName;
import mx.utils.ObjectUtil;
import mx.automation.IAutomationClass;
import mx.automation.IAutomationMethodDescriptor;
import mx.automation.IAutomationEventDescriptor;
import mx.automation.IAutomationPropertyDescriptor;

public class SeleFxAutomation implements IAutomationClass {

    private var _name:String;
    private var _superClassName:String;

    private var implementationClassNames:Array = [];
    private var properties:Array = [];
    private var verificationProperties:Array = [];
    private var descriptionProperties:Array = [];
    private var eventDescriptorMap:Object = {};
    private var methodDescriptorMap:Object = {};
    private var _propertyNameMap:Object = {};

    public function SeleFxAutomation(name:String, superClassName:String = null) {
        super();
        _name = name;
        _superClassName = superClassName;
    }

    public function addImplementationClassName(className:String):void {
        implementationClassNames.push(className);
    }

    public function addPropertyDescriptor(p:IAutomationPropertyDescriptor):void {
        properties.push(p);
        if (p.forVerification)
            verificationProperties.push(p);

        if (p.forDescription)
            descriptionProperties.push(p);

        _propertyNameMap[p.name] = p;
    }

    public function get superClassName():String {
        return _superClassName;
    }

    public function get name():String {
        return _name;
    }

    public function getPropertyDescriptors(objForInitialization:Object = null,
                                           forVerification:Boolean = true,
                                           forDescription:Boolean = true):Array {
        var result:Array = [];
        if (forVerification && forDescription)
            result = properties;
        else if (forVerification)
            result = verificationProperties;
        else if (forDescription)
            result = descriptionProperties;

        return result;
    }

    public function getDescriptorForEvent(
            event:Event):IAutomationEventDescriptor {
        var eventType:String = event.type;
        if (event is KeyboardEvent)
            eventType = "keyPress";
        var eventClassName:String = getClassName(event);
        var hash:String = hash(eventClassName, eventType);
        return hash in eventDescriptorMap ? eventDescriptorMap[hash] : null;
    }

    public function getDescriptorForMethodByName(
            methodName:String):IAutomationMethodDescriptor {
        for (var i:Object in methodDescriptorMap) {
            if (methodDescriptorMap[i].name == methodName)
                return methodDescriptorMap[i];
        }

        return null;
    }

    public function getDescriptorForEventByName(
            eventName:String):IAutomationEventDescriptor {
        for (var i:Object in eventDescriptorMap) {
            trace("eventDescriptorMap[i].name ["+eventDescriptorMap[i].name+"] eventName ["+eventName+"]");
            if (eventDescriptorMap[i].name == eventName)
                return eventDescriptorMap[i];
        }
        return null;
    }

    public function get propertyNameMap():Object {
        return _propertyNameMap;
    }

    public function addMethod(m:IAutomationMethodDescriptor):void {
        var hash:String = m.name;
        methodDescriptorMap[hash] = m;
    }

    public function addEvent(m:IAutomationEventDescriptor):void {
        var hash:String = hash(m.eventClassName, m.eventType);
        eventDescriptorMap[hash] = m;
    }

    public static function getClassName(obj:Object):String {
        return getQualifiedClassName(obj).replace("::", ".");
    }

    public static function getDefinitionFromObjectDomain(obj:Object, className:String):Class {
        var eventClass:Class ;
        try {
            eventClass = Class(ApplicationDomain.currentDomain.getDefinition(className));
        } catch(e:Error) {
            // we can get a reference or security exception
            // in which case we try to access the objects own domain
            try {
                var dispObj:DisplayObject = obj as DisplayObject;
                eventClass = Class(dispObj.root.loaderInfo.applicationDomain.getDefinition(className));
            } catch(e:Error) {
                // we can get a reference or security exception
                // In this case we assume that the class definition is not available
                // as the class has not been referenced in the app and it has
                // not been linked in.
            }
        }
        return eventClass;
    }

    private static function hash(eventClassName:String, eventType:String):String {
        return eventClassName.replace("::", ".") + "|" + eventType;
    }


    public function toString():String {
        return "name: " + _name + "\n" +
               "superClassName: " + _superClassName + "\n" +
               "eventDescriptorMap: " + ObjectUtil.toString(eventDescriptorMap);
    }
}
}